﻿using System;
using System.Collections.Generic;
using System.Text;
using OpenTK;

using OpenTK.Graphics.OpenGL;
using Xeno.Core;
using OpenTK;
using System.Diagnostics;

namespace Xeno.Module
{
    public class XenoMeshRenderer : XenoModule
    {

        public bool NoLighting { get; set; }
        public XenoMaterial Material { get; set; }
        public bool Sync { get; set; }
        private float[] Vertices;
        private float[] Normals;
        private float[] Tangents;
        private float[] BiNormals;
        private float[] Coords;
        private int[] Indices;
        private int List = 0;
        public bool DrawBounds { get; set; }
        


        public XenoMeshRenderer()
        {
        
            Name = "Mesh Renderer";
            ModuleAuthor = "Antony Wells";
            ModuleCopyright = "(c)Double Think Games 2009/10";
            ResetMeshRenderer();

        
        }
        
        public void ResetMeshRenderer()
        {

            Sync = true;
            DrawBounds = false;
            NoLighting = false;

        }

        public override void DoUpdate()
        {
            
            
        }
        private void RenderVertex(XenoVertex vertex)
        {
            GL.TexCoord3(vertex.Tex0);
            GL.Normal3(vertex.Normal);
            GL.Color4(vertex.Color);
            GL.Vertex3(vertex.Position);
        }
        public override void DoRender()
        {
     
            GL.PushAttrib(AttribMask.AllAttribBits) ;
            XenoMesh msh = Attached.Mesh;
            XenoMaterial useMat = null;
            if (XenoCore.MaterialOverride != null)
            {
                useMat = XenoCore.MaterialOverride;
            }
            else
            {
                useMat = Material;
            }
            useMat = Material;
            XenoDebug.AssertNull(useMat, "MeshRenderer.DoRender");
            XenoDebug.AssertNull(msh, "XenoMod_MeshRenderer.DoRender");
           

            GL.Enable(EnableCap.Blend);
            GL.Disable(EnableCap.AlphaTest);
            GL.Disable(EnableCap.ScissorTest);
            GL.Disable(EnableCap.Fog);
            GL.Disable(EnableCap.Lighting);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
           

            GL.MatrixMode(MatrixMode.Projection);
            Matrix4 projectionMatrix = XenoCore.CurrentView.Camera.Projection;
            GL.LoadMatrix(ref projectionMatrix);

            GL.MatrixMode(MatrixMode.Modelview);
            Matrix4 worldMatrix = Attached.WorldMatrix * XenoCore.CurrentView.WorldMatrix;
            GL.LoadMatrix(ref worldMatrix);
           

            Random rnd = new Random();
            if (Sync)
            {
                msh.UpdateNormals();
                Sync = false;
                int index = 0;
                Vertices = new float[msh.Vertices.Count * 3];
                Normals = new float[msh.Vertices.Count * 3];
                Coords = new float[msh.Vertices.Count * 3];
                Tangents = new float[msh.Vertices.Count * 3];
                BiNormals = new float[msh.Vertices.Count * 3];

                foreach (XenoVertex v in msh.Vertices)
                {

                    float[] verts = Vertices;
                    verts[index] = v.Position.X;
                    verts[index + 1] = v.Position.Y;
                    verts[index + 2] = v.Position.Z;

                    Normals[index] = v.Normal.X;
                    Normals[index + 1] = v.Normal.Y;
                    Normals[index + 2] = v.Normal.Z;

                    Coords[index] = v.Tex0.X;
                    Coords[index + 1] = v.Tex0.Y;
                    Coords[index + 2] = v.Tex0.Z;



                    Tangents[index] = v.Tangent.X;
                    Tangents[index + 1] = v.Tangent.Y;
                    Tangents[index + 2] = v.Tangent.Z;
              


                    BiNormals[index] =  v.BiNormal.X;
                    BiNormals[index + 1] = v.BiNormal.Y;
                    BiNormals[index + 2] = v.BiNormal.Z;

                    index += 3;
                   
                }

                Indices = new int[msh.Triangles.Count * 3];
                index = 0;
                foreach (XenoTriangle t in msh.Triangles)
                {
                    Indices[index] = t.V1;
                    Indices[index + 1] = t.V2;
                    Indices[index + 2] = t.V3;
                    index += 3;
                }

            }

          
           
            GL.EnableClientState(EnableCap.VertexArray);
            GL.EnableClientState(EnableCap.TextureCoordArray);
            GL.EnableClientState(EnableCap.NormalArray);
         
            GL.VertexPointer<float>(3, VertexPointerType.Float, 0, Vertices);
            GL.NormalPointer<float>(NormalPointerType.Float, 0, Normals);
            GL.TexCoordPointer<float>(3, TexCoordPointerType.Float, 0, Coords);
          

            var first = true;
            if (XenoCore.LightList.Count>0 && NoLighting == false)
            {
                GL.Enable(EnableCap.DepthTest);
                GL.DepthMask(true);
                GL.DepthFunc(DepthFunction.Lequal);
                DrawMesh();
                GL.DepthFunc(DepthFunction.Equal);
                //GL.DepthMask(false);
            }
            else
            {

            }
            GL.VertexAttribPointer<float>(6, 3, VertexAttribPointerType.Float, false, 0, Tangents);
            GL.VertexAttribPointer<float>(7, 3, VertexAttribPointerType.Float, false, 0, BiNormals);
            //  GL.VertexAttribPointer(

         
            GL.EnableVertexAttribArray(6);
            GL.EnableVertexAttribArray(7);
           // XenoDebug.LogError("Lights:" + Global.LightList.Count,"");
            if (XenoCore.LightList.Count == 0 || NoLighting)
            {
                RenderMesh(useMat);
            }
            else
            {
            //    System.Environment.Exit(-1);
                foreach (var light in XenoCore.LightList)
                {


                    XenoCore.CurrentLight = light;

                    RenderMesh(useMat);
                    if (first)
                    {
                        GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusDstColor);
                        first = false;
                    }

                }

            }
            GL.DisableClientState(EnableCap.VertexArray);
            GL.DisableClientState(EnableCap.NormalArray);
            GL.DisableClientState(EnableCap.TextureCoordArray);

            GL.DisableVertexAttribArray(6);
            GL.DisableVertexAttribArray(7);


            if (DrawBounds)
            {
                GL.DepthFunc(DepthFunction.Lequal);

                GL.Disable(EnableCap.Blend);


                DrawBounds3D(msh.BoundingBox);
            }
            GL.PopAttrib();

        }

        private void RenderMesh(XenoMaterial useMat)
        {
            useMat.WorldMatrix = Attached.WorldMatrix;
            useMat.WorldRotationMatrix = Attached.WorldRotationMatrix;
            if (useMat != null)
            {
                useMat.Bind();
            }

            DrawMesh();
            if (useMat != null)
            {
                //  Environment.Exit(-1);
                useMat.Unbind();
            }
            else
            {
                //    Environment.Exit(-1);
            }
        }
        public static void DrawBounds3D(Vector3[] bb)
        {
            GL.Color4(System.Drawing.Color.White);

            GL.LineWidth(4);

            GL.Begin(BeginMode.Lines);

            GL.Vertex3(bb[0]);
            GL.Vertex3(new Vector3(bb[1].X, bb[0].Y, bb[0].Z));

            GL.Vertex3(bb[0]);
            GL.Vertex3(new Vector3(bb[0].X,bb[1].Y,bb[0].Z));

            GL.Vertex3(bb[0]);
            GL.Vertex3(new Vector3(bb[0].X, bb[0].Y, bb[1].Z));


            GL.End();
        }
        private void DrawMesh()
        {
          GL.DrawElements<int>(BeginMode.Triangles, Indices.Length, DrawElementsType.UnsignedInt, Indices);
            //GL.DrawArrays(BeginMode.Triangles, 0, Attached.Mesh.Triangles.Count);
            //GL.DrawRangeElements<int>(BeginMode.Triangles, 0, Attached.Mesh.Triangles.Count * 3, Attached.Mesh.Triangles.Count*3, DrawElementsType.UnsignedInt, Indices);



        }

    }
}
